/*
 * Copyright (c) 2011, Julien Lamy
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * Neither the name of the project's author nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include "chunkreader.h"

#include <cstring>
#include <istream>
#include <sstream>
#include <stdexcept>

ChunkReader::ChunkReader(std::istream & stream, char const* wanted_type)
: stream_(stream), header_length_(0), total_length_(0), bytes_read_(0),
  header_length_read_(false), done_(false)
{
    static char type_in_stream[5] = {0,0,0,0,0};
    this->read(type_in_stream, 4);

    if(std::strcmp(wanted_type, type_in_stream)!=0)
    {
        std::ostringstream message;
        message << "Expected " << wanted_type << ", got " << type_in_stream;
        throw std::runtime_error(message.str());
    }

    this->read(this->header_length_);
    this->header_length_read_ = true;

    this->read(this->total_length_);

    // Do not check total_length vs header_length, since total_length might be
    // a number of children as well as the byte length.
}

uint32_t ChunkReader::header_length() const
{
    return this->header_length_;
}

uint32_t ChunkReader::total_length() const
{
    return this->total_length_;
}

unsigned long ChunkReader::bytes_read() const
{
    return this->bytes_read_;
}

bool ChunkReader::done() const
{
    return this->done_;
}

void ChunkReader::ignore_rest_of_header()
{
    this->stream_.ignore(this->header_length_-this->bytes_read_);
    this->bytes_read_ = this->header_length_;
    this->done_ = true;
}

void ChunkReader::read_(char* destination, unsigned int size)
{
    if(this->header_length_read_ && this->bytes_read_+size>this->header_length_)
    {
        std::ostringstream message;
        message << size << " bytes requested, only "
                << this->header_length_-this->bytes_read_ << " remaining";
        throw std::runtime_error(message.str());
    }

    this->stream_.read(destination, size);
    unsigned int const bytes_read = this->stream_.gcount();
    if(bytes_read != size)
    {
        std::ostringstream message;
        message << "Expected " << size << " bytes, read " << bytes_read;
        throw std::runtime_error(message.str());
    }

    this->bytes_read_ += bytes_read;

    if(this->header_length_read_ && this->bytes_read_ == this->header_length_)
    {
        this->done_ = true;
    }
}
